package rmi.management.client;

import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Scanner;



import org.apache.log4j.Logger;

import rmi.analytics.server.AnalyticsInterface;

import rmi.billing.domain.BillPosition;
import rmi.billing.domain.PriceStep;
import rmi.billing.domain.PriceSteps;
import rmi.billing.exceptions.InvalidPriceStepRemoteException;
import rmi.billing.exceptions.LoginFailedRemoteException;
import rmi.billing.exceptions.NegativeValuesRemoteException;
import rmi.billing.exceptions.PriceIntervallCollisionRemoteException;
import rmi.billing.exceptions.PriceStepNotExistsRemoteException;
import rmi.billing.server.BillingServerInterface;

import rmi.billing.server.BillingServerSecure;

import rmi.domain.RemoteClientInterface;
import rmi.domain.RemoteRegistrySingleton;
import rmi.eventing.types.Event;
import socket.domain.SplittLine;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class ManagementClient implements RemoteClientInterface{
	/*
	 * Manages userinput for anaylics and billingserver
	 * eventOutput and billingparts
	 */
	public static Logger log = Logger.getLogger(ManagementClient.class);
	private ArrayList<Event> eventBuffer = new ArrayList<Event>();
	private boolean mode = false; 
	private ArrayList<String> usedEvents = new ArrayList<String>();

	public ManagementClient(){
		super();
	}

	public static void main(String[] args) {

		String analyticsref="";
		String billingreg="";
		//Parametervalidation
		if(args.length==2){
			analyticsref=args[0];
			billingreg=args[1];
		}
		else{
			//Usage
			System.out.println("Usage: java ManagementClient <analytics.bindingName> <billing.bindingName>");
			return;
		}
		Registry reg;
		try {
			reg = RemoteRegistrySingleton.getRemoteRegistry();
		} catch (RemoteException e1) {
			System.out.println("Can't get RemoteRegistry with given properties!");
			System.out.println("ManagementClient closed...");
			return;
		}

		//Instance of MC
		ManagementClient mc = new ManagementClient();

		AnalyticsInterface anServ=null;
		RemoteClientInterface stub=null;
		BillingServerInterface billServ=null;

		//List with subscriptions the client made for unsubscribing only his subsriptions
		ArrayList<String> subscriptionids = new ArrayList<String>();

		BillingServerSecure billSecure =   null;
		String username_mem="";

		boolean loggedin=false;
		try {
			//Analyticserverstub from Registry
			anServ =(AnalyticsInterface) reg.lookup(analyticsref);
			//Export MC
			stub = (RemoteClientInterface) UnicastRemoteObject.exportObject(mc,0);

		} catch (RemoteException e) {
			System.out.println("Error: Can't connect to AnalyticsServerReference, analyticspart won't work");
		} catch (NotBoundException e) {
			System.out.println("Error: Can't find AnalyticsServerReference, analyticspart won't work");
		}
		try {
			//BillingServerStub from registry
			billServ =(BillingServerInterface) reg.lookup(billingreg);
		} catch (RemoteException e) {
			System.out.println("Error: Can't connect to BillingServerReference, billing part won't work");
		} catch (NotBoundException e) {
			System.out.println("Error: Can't find BillingServerReference, billing part won't work");
		}

		//Userinput
		Scanner scanner = new Scanner(System.in);
		String input = "";
		boolean resume=true;
		while(resume){
			System.out.print(">");
			input = scanner.nextLine();
			//Shutdown MC
			if(input.equals("!exit")){
				System.out.println("Shutting down ManagementClient!");
				resume=false;
				try {
					UnicastRemoteObject.unexportObject(mc, true);
				} catch (NoSuchObjectException e) {
					log.error("Client was not exportet");
				}

			}
			//subscribe to Analytics (Regexfilter necessary)
			else if (input.matches("!subscribe.*")){
				if(input.length()>11){
					String regex = input.substring(11);
					regex = regex.replace(" ","");
					if(!(anServ==null)){
						try {
							Pattern.compile(regex);
							Integer x = anServ.subscribe(regex, stub);
							System.out.println("Created subscription with ID "+ x+" for events using filter "+regex);
							subscriptionids.add(x.toString());

						} catch (PatternSyntaxException exception) {
							System.out.println("ERROR: No valid filter set");  
						} catch (RemoteException e) {
							System.out.println("Error: AnalyticsServer is not available anymore");
							log.debug("Setting AnalyticsServerReference to null");
							anServ=null;
						}
					}
					else{
						System.out.println("Error: AnalyticsServer is not available");
					}
				}
				else{
					System.out.println("Usage: !subscribe <regex>");
				}

			}
			//unsubscribe from analytics (subscriptionID necessary)
			else if (input.matches("!unsubscribe.*")){
				if(input.length()>13){
					String id = input.substring(13);
					id = id.replace(" ","");
					if(!(anServ==null)){
						try{
							Integer cid = Integer.valueOf(id);
							//check if the client made this subscription
							if(subscriptionids.contains(id)){
								try {
									System.out.println(anServ.unsubscribe(cid));
									subscriptionids.remove(id);
								} catch (RemoteException e) {
									System.out.println("Error: AnalyticsServer is not available anymore");
									log.debug("Setting AnalyticsServerReference to null");
								}

							}
							else{
								System.out.println("Access denied! Subscription with ID "+ cid + " was not created by this client.");
							}
						}
						catch(NumberFormatException ex){
							System.out.println(id + " is o valid ID to unsubscribe");
						}
					}
					else{
						System.out.println("Error: AnalyticsServer is not available");
					}	
				}
				else{
					System.out.println("Usage: !unsubscribe <SubscriptionID>");
				}

			}
			//Eventoutput automatically
			else if (input.equals("!auto")){
				if (mc.getMode()){
					System.out.println("Mode is already 'auto'");
				}
				else{
					ArrayList<Event> evb = mc.getEventBuffer();
					for (int i =0; i<evb.size();i++){
						System.out.println(evb.get(i));
					}
					mc.setMode(true);
					mc.resetEventBuffer();
					
				}
			}
			//hide Eventoutput
			else if (input.equals("!hide")){
				if (!mc.getMode()){
					System.out.println("Mode is already 'hide'");
				}
				else{
					mc.setMode(false);
				}
			}
			//Print Buffered Events
			else if (input.equals("!print")){
				ArrayList<Event> evb = mc.getEventBuffer();
				for (int i =0; i<evb.size();i++){
					System.out.println(evb.get(i));
				}
				mc.resetEventBuffer();

			}
			//Login to BillingServer (Username Password required)
			else if (input.matches("!login.*")){
				if(!(billServ==null)){
					if(!loggedin){	
						SplittLine cutinput = new SplittLine(input);
						String temp = cutinput.readNext();
						String username="";
						String pw = ""; 
						if (cutinput.hasNext()){
							username = cutinput.readNext();
							if (cutinput.hasNext()){
								pw = cutinput.readNext();
								try {
									//If username and pw correct get BillingSecurestub
									billSecure = billServ.login(username, pw);
									System.out.println(username + " successfully logged in ");
									username_mem = username;
									loggedin = true;	
								}catch (LoginFailedRemoteException le){
									System.out.println("Login was not successfull");

								} catch (RemoteException e) {
									System.out.println("ERROR: BillingServer is not available anymore");
									log.debug("Set billServ to null");
								}
							}	
							else{
								System.out.println("Usage: !login <username> <password>");
							}
						}
						else{
							System.out.println("Usage: !login <username> <password>");
						}
					}
					else{
						System.out.println("ERROR: You are currently logged in.");
					}
				}
				else{
					System.out.println("ERROR: BillingServer ist not available!");
				}

			}
			//add billingstep
			else if (input.matches("!addStep.*")){
				if(loggedin){	
					SplittLine cutinput = new SplittLine(input);
					String temp = cutinput.readNext();
					double a,b,c,d=0;
					try{
						if(cutinput.hasNext()){
							a = Double.valueOf(cutinput.readNext());
							if(cutinput.hasNext()){
								b = Double.valueOf(cutinput.readNext());
								if(cutinput.hasNext()){
									c = Double.valueOf(cutinput.readNext());
									if(cutinput.hasNext()){
										d = Double.valueOf(cutinput.readNext());
										try {
											billSecure.createPriceStep(a, b, c, d);
											if (b==0)
												System.out.println("Step ["+a+" INFINITY] successfully added");
											else
												System.out.println("Step ["+a+" "+b+"] successfully added");
										} catch (RemoteException e) {
											System.out.println("ERROR: BillingServer is not available anymore");
											log.debug("Set loggedin to false");
											loggedin = false;
										} catch (NegativeValuesRemoteException e) {
											System.out.println("ERROR: Values have to be positive");
										} catch (InvalidPriceStepRemoteException e) {
											System.out.println("ERROR: No valid pricestep choosen");
										} catch (PriceIntervallCollisionRemoteException e) {
											System.out.println("ERROR: Priceintervall collision occured");
										}

									}
									else{
										System.out.println("Usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
									}
								}
								else{
									System.out.println("Usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
								}
							}
							else{
								System.out.println("Usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
							}
						}
						else{
							System.out.println("Usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
						}
					}
					catch(NumberFormatException ex){
						log.error("!addStep wrong Parameter");
						System.out.println("Usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
					}	
				}
				else{
					System.out.println("ERROR: You are currently not logged in.");
				}

			}
			//Show billingsteps
			else if (input.equals("!steps")){
				if(loggedin){
					PriceSteps steps;
					try {
						steps = billSecure.getPriceSteps();
						ArrayList<PriceStep> ps = steps.getSteps();
						ArrayList<PriceStep> sort = new ArrayList<PriceStep>();
						int z=0;
						while(z<ps.size()){
							PriceStep smallest = ps.get(z);
							for (int x=0;x<ps.size();x++){
								if(smallest.getStartPrice()>ps.get(x).getStartPrice()){
									smallest=ps.get(x);
								}
							}
							z=0;
							sort.add(smallest);
							ps.remove(smallest);
						}
						if(sort.size()>0)
						{
							System.out.println("Min_Price  Max_Price  Fee_Fixed  Fee_Variable");
	
							for(PriceStep s : sort){
								String min =String.valueOf(s.getStartPrice());
								int length = min.length();
								for (int i=0; i<(11-length);i++){
									min+=" ";
								}
								String max =String.valueOf(s.getEndPrice());
								if(max.equals("0.0")){
									max = "INFINITY";
								}
								length = max.length();
								for (int i=0; i<(11-length);i++){
									max+=" ";
								}
								String fee = String.valueOf(s.getFixedPrice());
								length = fee.length();
								for (int i=0; i<(11-length);i++){
									fee+=" ";
								}
								System.out.println(min+max+fee+s.getVariablePricePercent()+"%");
							}
						}
						else
							System.out.println("There are no price steps available");
						
					} catch (RemoteException e) {
						System.out.println("ERROR: BillingServer is not available anymore");
						log.debug("Set loggedin to false");
						loggedin = false;
					}
				}
				else{
					System.out.println("ERROR: You are currently not logged in.");
				}

			}
			//remove billingsteps
			else if (input.matches("!removeStep.*")){
				if (loggedin){
					SplittLine cutinput = new SplittLine(input);
					String temp = cutinput.readNext();
					String s="";
					double a,b=0;
					if (cutinput.hasNext()){
						try{
							a = Double.valueOf(cutinput.readNext());
							if(cutinput.hasNext()){
								s=cutinput.readNext();
								if(s.equals("0.0")||s.equals("INFINITY")){
									b=0;
								}
								else
									b = Double.valueOf(s);
								try {
									billSecure.deletePriceStep(a,b);
									if(b==0)
										System.out.println("Price step ["+a+" INFINITY] successfully removed ");
									else
										System.out.println("Price step ["+a+" "+b+"] successfully removed ");
								} catch (RemoteException e) {
									System.out.println("ERROR: BillingServer is not available anymore");
									log.debug("Set loggedin to false");
									loggedin = false;
								} catch (PriceStepNotExistsRemoteException e) {
									System.out.println("ERROR: Price step ["+a+" "+b+"] does not exist");
								}	
							}
							else{
								System.out.println("Usage: !removeStep <startPrice> <endPrice>");
							}
						}
						catch(NumberFormatException ex){
							log.error("!removeStep wrong Parameter");
							System.out.println("Usage: !removeStep <startPrice> <endPrice>");
						}


					}
					else{
						System.out.println("Usage: !removeStep <startPrice> <endPrice>");
					}
				}
				else{
					System.out.println("ERROR: You are currently not logged in.");
				}

			}
			//print bill for username
			else if (input.matches("!bill.*")){
				if(input.length()>6)
				{
					if(loggedin){
						String name = input.substring(6);
						name = name.replace(" ","");
						ArrayList<BillPosition> positions;
						try {

							positions = billSecure.getBill(name).getBillPositions();
							ArrayList<BillPosition> ps = positions;
							ArrayList<BillPosition> sort = new ArrayList<BillPosition>();
							int z=0;
							while(z<ps.size()){
								BillPosition smallest = ps.get(z);
								for (int x=0;x<ps.size();x++){
									if(smallest.getAuctionID()>ps.get(x).getAuctionID()){
										smallest=ps.get(x);
									}
								}
								z=0;
								sort.add(smallest);
								ps.remove(smallest);
							}

							if(sort.size()>0)
							{
								System.out.println("auction_ID strike_price fee_fixed fee_variable fee_total");
	
								for(BillPosition s : sort){
									String id =String.valueOf(s.getAuctionID());
									int length = id.length();
									for (int i=0; i<(11-length);i++){
										id+=" ";
									}
									String price =String.valueOf(s.getStrikePrice());
	
									length = price.length();
									for (int i=0; i<(13-length);i++){
										price+=" ";
									}
									String fee = String.valueOf(s.getFeeFixed());
									length = fee.length();
									for (int i=0; i<(10-length);i++){
										fee+=" ";
									}
									String feevariable = String.valueOf(s.getFeeVariable());
									length = feevariable.length();
									for (int i=0; i<(13-length);i++){
										feevariable+=" ";
									}
									System.out.println(id+price+fee+feevariable+s.getFeeTotal());
								}
							}
							else
								System.out.println("There is no bill available for "+name);
							
						} catch (RemoteException e) {
							System.out.println("ERROR: BillingServer is not available anymore");
							log.debug("Set loggedin to false");
							loggedin = false;
						}
					}
					else{
						System.out.println("ERROR: You are currently not logged in.");
					}
				}else
					System.out.println("Usage: !bill <userName>");

			}
			//logout
			else if (input.equals("!logout")){
				if(loggedin){
					billSecure = null;
					loggedin=false;
					System.out.println(username_mem+" successfully logged out");
				}
				else{
					System.out.println("ERROR: You are currently not logged in.");
				}

			}
			else{
				System.out.println("Unknown Command!");
			}
		}


	}

	@Override
	public void processEvent(Event ev) throws RemoteException {
		synchronized(usedEvents){
			if (usedEvents.contains(ev.getID())){
				usedEvents.remove(ev.getID());
				usedEvents.add(ev.getID());
			}
			else{
				if(mode){
					System.out.println("");
					System.out.println(ev.toString());
				}
				else{
					eventBuffer.add(ev);
				}
				if(usedEvents.size()>=1000){
					usedEvents.remove(0);
					usedEvents.add(ev.getID());
				}
				else{
					usedEvents.add(ev.getID());
				}
			}
		}

	}
	public void setMode(boolean mode){
		this.mode = mode;
	}
	public boolean getMode(){
		return mode;
	}
	public ArrayList<Event> getEventBuffer(){
		return eventBuffer;
	}
	public void resetEventBuffer(){
		eventBuffer = new ArrayList<Event>();
	}



}
